﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.Dynamic;
using System.Collections.Specialized;

namespace CaliburnWpf.Views.Infrastructure
{
    public class DynamicNotifyableWrapper:DynamicObject,INotifyable,INotifyPropertyChanged,IWrapper,ICollectionNotificationRouter
    {

        public void OnChange(string prop)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private static readonly Dictionary<string, Dictionary<string, PropertyInfo>> properties =
            new Dictionary<string, Dictionary<string, PropertyInfo>>();
        private readonly object instance;
        private readonly string typeName;

        public DynamicNotifyableWrapper(object instance)
        {
            this.instance = instance;
            var type = instance.GetType();
            typeName = type.FullName;
            if (!properties.ContainsKey(typeName))
                SetProperties(type, typeName);
        }

        private static void SetProperties(Type type, string typeName)
        {
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var dict = props.ToDictionary(prop => prop.Name);
            properties.Add(typeName, dict);
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (properties[typeName].ContainsKey(binder.Name))
            {
                result = properties[typeName][binder.Name].GetValue(instance, null);
                return true;
            }
            result = null;
            return false;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            if (properties[typeName].ContainsKey(binder.Name))
            {
                properties[typeName][binder.Name].SetValue(instance, value, null);
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(binder.Name));
                return true;
            }
            return false;
        }

        #region IWrapper Members

        public object Instance
        {
            get { return this.instance; }
        }

        #endregion



        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #endregion

        #region INotifyableCollection Members

        public void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
        {
            if (CollectionChanged != null)
                CollectionChanged(this,args);
        }

        #endregion
    }
}
